home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / DMAKE38B.ARJ / EXEC.ASM < prev    next >
Assembly Source File  |  1991-06-10  |  37KB  |  1,232 lines

  1. ; DESCRIPTION
  2. ;      This code is a model independent version of DOS exec that will swap
  3. ;      the calling process out to secondary storage prior to running the
  4. ;      child.  The prototype for calling the exec function is below.
  5. ;
  6. ;      exec( int swap, char far *program, char far *cmdtail,
  7. ;         int environment_seg, char far *tmpfilename );
  8. ;
  9. ;
  10. ;      To assemble this file issue the command:
  11. ;
  12. ;         tasm /mx /t /dmmodel exec.asm
  13. ;
  14. ;      where 'model' is one of {small, compact, medium, large}, you may
  15. ;      also use MASM 5.1 to assemble this file, in this case simply replace
  16. ;      'tasm' with 'masm' in the above command line.
  17. ;
  18. ; AUTHOR
  19. ;      Dennis Vadura, dvadura@watdragon.uwaterloo.ca
  20. ;      CS DEPT, University of Waterloo, Waterloo, Ont., Canada
  21. ;
  22. ; COPYRIGHT
  23. ;      Copyright (c) 1990 by Dennis Vadura.  All rights reserved.
  24. ;      This program is free software; you can redistribute it and/or
  25. ;      modify it under the terms of the GNU General Public License
  26. ;      (version 1), as published by the Free Software Foundation, and
  27. ;      found in the file 'LICENSE' included with this distribution.
  28. ;      This program is distributed in the hope that it will be useful,
  29. ;      but WITHOUT ANY WARRANTY; without even the implied warrant of
  30. ;      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  31. ;      GNU General Public License for more details.
  32. ;      You should have received a copy of the GNU General Public License
  33. ;      along with this program;  if not, write to the Free Software
  34. ;      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  35. ;
  36. ifdef have286
  37.  .286    ; define have286 with -D for 80286 processor or better
  38.     mpusha Macro
  39.         pusha
  40.     Endm
  41.  
  42.     mpopa Macro
  43.         popa
  44.     Endm
  45.  
  46. else    ; 8088/8086 compatible
  47.     mpusha Macro
  48.         push ax
  49.     push cx
  50.     push dx
  51.     push bx
  52.     push sp
  53.     push bp
  54.     push si
  55.     push di
  56.     Endm
  57.  
  58.     mpopa Macro
  59.         pop di
  60.     pop si
  61.     pop bp
  62.     add sp,2
  63.     pop bx
  64.     pop dx
  65.     pop cx
  66.     pop ax
  67.     Endm
  68. endif
  69.  
  70. ifdef msmall
  71.             .model    small
  72. argbase        equ    4
  73. endif
  74. ifdef mcompact
  75.         .model  compact
  76. argbase        equ    4
  77. endif
  78. ifdef mmedium
  79.         .model    medium
  80. argbase        equ    6
  81. endif
  82. ifdef mlarge
  83.         .model    large
  84. argbase        equ    6
  85. endif
  86. a_swap        equ    <bp+argbase+0>
  87. a_prog        equ    <bp+argbase+2>
  88. a_tail        equ    <bp+argbase+6>
  89. a_env         equ    <bp+argbase+10>
  90. a_tmp        equ    <bp+argbase+12>
  91.  
  92. a_handle    equ    <bp+argbase>
  93.  
  94.  
  95. ; Define all useful equ's
  96. swap_xms    equ    0        ; we swapped it out to xms
  97. swap_ems    equ    2        ; we swapped it out to ems
  98. swap_file    equ    4        ; we swapped it out to a file
  99. seg_no_alloc    equ    0        ; this is part of a segment
  100. seg_alloc    equ    1        ; this is a full segment header
  101. seg_data    equ    2        ; this is data for part of a segment
  102.  
  103.  
  104. ; Define any global/external variables that we will be accessing from here.
  105.             .data
  106.         extrn    _errno:word        ; Set to dos ret code from exec
  107.         public  _Interrupted        ; Set to 1 if interrupted 0
  108. _Interrupted    dw    0            ; otherwise
  109.  
  110.             .code
  111.         assume    cs:@code, ds:@code, ss:@code, es:@code
  112.  
  113.         even
  114. execstack    dw    64  dup (?)    ; put the temporary exec stack right
  115. exec_sp        label    word        ; at the start.
  116.  
  117. old_ss        dw    ?        ; save stack seg across exec
  118. old_sp        dw    ?        ; save stack ptr across exec
  119. progsize    dw    ?        ; original size of the program
  120. rootsize    dw    ?        ; size of base root kept during swap
  121. resend        dw    ?        ; paragraph where resident code ends
  122. envseg        dw    ?        ; paragraph of environment segment
  123. psp        dw    ?        ; our own psp
  124. swap        dw    ?        ; swapping selection flag
  125. eretcode    dw    ?        ; return code from exec
  126. interrupted    dw    ?        ; interrupted flag for exec
  127. arenahead    dw    ?        ; start of memory block list
  128. alstr        dw    ?        ; allocation strategy save spot
  129. in_exec        dw    0        ; flag, 1 ==> in exec
  130.  
  131. cmdpath        db    65  dup(?)    ; file to exec
  132. cmdtail        db    129 dup(?)    ; its command tail
  133. fcb        db    37  dup(0)    ; dummy fcb
  134. tmpseg        db    7   dup(?)    ; block header buffer
  135.  
  136. tmpname        db    65  dup(0)    ; name of temporary file resource
  137.  
  138.         even
  139. tmphandle    dw    ?        ; handle for temporary file
  140. real_21h    dd    0        ; will be DOS's 21h vector if doing -C
  141.  
  142. std_fil_handle    dw    ?        ; file handle for -C file
  143. std_fil_number    db    ?        ; system file number for -C file
  144. our_stdout    db    ?        ; sys file number our stdout handle
  145.  
  146. error_rhdr    db    "exec: Failure reading header block", 0DH, 0AH, '$'
  147. error_rseg    db    "exec: Failure reading segment data", 0DH, 0AH, '$'
  148. error_resize    db    "exec: Failure on resize", 0DH, 0AH, '$'
  149. error_free    db    "exec: Failure to free a block", 0DH, 0AH, '$'
  150. error_string    db    "exec: Program swap failure", 0DH, 0AH, '$'
  151. error_alloc    db    "exec: Memory blocks don't match", 0DH, 0AH, '$'
  152.  
  153.         even
  154. write_header label word
  155.    whdr_xms_ptr        dw    word ptr whdr_xms
  156.    whdr_ems_ptr        dw    word ptr whdr_ems
  157.    whdr_file_ptr    dw    word ptr whdr_file
  158.  
  159. write_seg label word
  160.    wseg_xms_ptr        dw    word ptr wseg_xms
  161.    wseg_ems_ptr        dw    word ptr wseg_ems
  162.    wseg_file_ptr    dw    word ptr wseg_file
  163.  
  164. read_header label word
  165.    rhdr_xms_ptr        dw    word ptr rhdr_xms
  166.    rhdr_ems_ptr        dw    word ptr rhdr_ems
  167.    rhdr_file_ptr    dw    word ptr rhdr_file
  168.  
  169. read_seg label word
  170.    rseg_xms_ptr        dw    word ptr rseg_xms
  171.    rseg_ems_ptr        dw    word ptr rseg_ems
  172.    rseg_file_ptr    dw    word ptr rseg_file
  173.  
  174. free_resource label word
  175.    free_xms_ptr        dw    word ptr free_xms_resource
  176.    free_ems_ptr        dw    word ptr free_ems_resource
  177.    free_file_ptr    dw    word ptr free_file_resource
  178.  
  179. reset_resource label word
  180.    reset_xms_ptr    dw    word ptr reset_xms_resource
  181.    reset_ems_ptr    dw    word ptr reset_ems_resource
  182.    reset_file_ptr    dw    word ptr reset_file_resource
  183.  
  184. old_ctl_brk label dword
  185.    old_ctl_brk_off    dw    ?
  186.    old_ctl_brk_seg     dw    ?
  187.  
  188. old_crit_err label dword
  189.    old_crit_err_off    dw    ?
  190.    old_crit_err_seg     dw    ?
  191.  
  192. exec_block label word
  193.   ex_envseg    dw    ?            ; env seg, use parent's if 0
  194.   ex_cmdtail    dd    ?            ; command tail for exec
  195.   ex_fcb1    dd    far ptr fcb        ; fcb's aren't used by dmake
  196.   ex_fcb2    dd    far ptr fcb
  197.   ex_ss        dw    ?            ; saved ss for exec
  198.   ex_sp        dw    ?            ; saved sp for exec
  199.   ex_error    dw    0            ; error code for dos exec
  200.  
  201.  
  202. ; Special 21h (DOS call) handler to tee stdout/stderr writes to the -C file.
  203. ; Ignore 21h calls that aren't writes to 1 or 2; i.e., pass them to DOS handler.
  204. ; If write call was from this process, it's pretty simple to duplicate it
  205. ; to the -C file.  If it's from another process, we try to write to its
  206. ; inherited handle.  Worst case is where the handle wasn't inherited: someone
  207. ; closed it.  In that instance we have to switch to dmake's PSP to do the
  208. ; duplicate write.
  209.  
  210. ; Subprocesses do not get their stdout/stderr teed to the -C file if
  211. ; their stdout/stderr no longer points to the file/device that dmake's
  212. ; stdout points to.  This is tested by looking at the process's job
  213. ; file table, which is a table that maps process handles to DOS system file
  214. ; table numbers.  (The far pointer to the JFT is at the PSP offset 34h.)
  215. ; The JFT is also queried to see if the -C file was inherited.
  216.  
  217. ; O_BINARY, O_TEXT problems are ignored here.  These are fudged by the
  218. ; C library before it calls DOS; since we're working below that level
  219. ; we don't have to worry about it.
  220.  
  221. simulate_21h Macro
  222.     pushf            ;; direct call to DOS
  223.     call cs:[real_21h]
  224.     Endm
  225.  
  226.     assume cs:@code, ds:nothing, es:nothing, ss:nothing
  227. our_21h_handler proc far
  228.     pushf
  229.     cmp ah,40h        ; is this a write?
  230.     jne call_dos    ; --no
  231.     cmp bx,1        ; write on handle 1 (stdout?)
  232.     je duplicate_it
  233.     cmp bx,2        ; stderr?
  234.     je duplicate_it
  235.  
  236. call_dos:
  237.     popf
  238.     jmp [real_21h]    ; far jump to real handler, which will do the sys call
  239.                 ; and return to the original caller
  240.  
  241. duplicate_it:
  242.     mpusha
  243.     push ds
  244.     push es
  245.     mov bp,sp
  246.  
  247.     mov di,std_fil_handle    ; handle of the -C file
  248.  
  249.   If @codesize eq 0
  250.       ; Small/compact models allow for quick test of us versus subprocess.
  251.     ; False negative (it's us with a different CS) will be picked
  252.     ; up by code just below.  (Might happen due to call from C library.)
  253.     ; False positives would be bad, but can't happen.
  254.     mov ax,[bp+24]    ; caller's CS
  255.     cmp ax,@code    ; same as us?
  256.     je call_from_dmake
  257.   Endif
  258.  
  259.     mov ah,51h        ; get PSP ("undocumented version" works in DOS 2.0+)
  260.     simulate_21h    ; PSP segment returned in BX
  261.     cmp bx,psp        ; our PSP?
  262.     je call_from_dmake    ; --yes, no PSP changing needed
  263.  
  264.     mov es,bx        ; set ES to current (caller's) PSP
  265.     lds bx,es:[34h]    ; set DS:BX pointing to caller's job file table
  266.  
  267.     mov si,[bp+12]    ; file handle caller passed in (known to be 1 or 2)
  268.     mov al,[bx+si]    ; system file number corresponding to caller's handle
  269.     cmp al,our_stdout    ; same as our stdout?
  270.     jne do_real_write    ; no--subprocess must have redirected it
  271.  
  272.     mov al,[bx+di]    ; see if caller has dup of -C file still open
  273.     cmp al,std_fil_number
  274.     je use_dup        ; yes--we can write using caller's PSP
  275.  
  276.         ; Calling process (or some intermediate process) has closed
  277.     ; the -C descriptor.  We'll use dmake's (our) -C descriptor, but
  278.     ; to do so we'll have to change the PSP.  Disable BREAK handling
  279.     ; so that ^break doesn't kill the wrong process.
  280.  
  281.     mov ax,3300h    ; get BREAK flag
  282.     simulate_21h
  283.     mov si,dx        ; save BREAK state in SI
  284.     sub dx,dx        ; now turn break flag off
  285.     mov ax,3301h
  286.     simulate_21h    ; don't want ^Break recoginized while PSP changed
  287.     mov bx,psp        ; set dmake's PSP
  288.     mov ah,50h
  289.     simulate_21h
  290.  
  291.     mov bx,di            ; handle of -C file
  292.     ; CX still has caller's count
  293.     mov ds,[bp+2]        ; restore caller's DS
  294.     mov dx,[bp+14]        ; DS:DX again points to caller's buffer
  295.     mov ah,40h
  296.     simulate_21h        ; write the copy
  297.  
  298.     mov bx,es        ; caller's PSP
  299.     mov ah,50h        ; set PSP
  300.     simulate_21h    ; restore caller's PSP
  301.     mov dx,si        ; break state before we changed it
  302.     mov ax,3301h
  303.     simulate_21h    ; restore break state
  304.  
  305.     jmp short do_real_write
  306.  
  307. use_dup:
  308.     mov ds,[bp+2]        ; restore caller's DS
  309.     mov dx,[bp+14]        ; DS:DX again points to caller's buffer
  310.  
  311. call_from_dmake:
  312.     mov bx,di            ; handle of -C file
  313.     mov ah,40h            ; write
  314.     ; CX still has caller's count
  315.     simulate_21h        ; write to the file
  316.  
  317. do_real_write:
  318.     pop es
  319.     pop ds
  320.     mpopa
  321.     popf
  322.     jmp [real_21h]    ; far jump to real handler, which will do the sys call
  323.                 ; and return to the original caller
  324. our_21h_handler endp
  325.  
  326.     assume    cs:@code, ds:@code, ss:@code, es:@code
  327.  
  328. ;-----------------------------------------------------------------------------
  329. ; First define the critical-error and control-brk handlers. 
  330. ; The critical error handler simply pops the machine state and returns an
  331. ; access denied result code.
  332. crit_err_handler proc far
  333.         add    sp, 6        ; ip/cs/flags ...
  334.         pop    ax
  335.         pop    bx
  336.         pop    cx
  337.         pop    dx
  338.         pop    si
  339.         pop    di
  340.         pop    bp
  341.         pop    ds
  342.         pop    es
  343.         push    bp        ; fix up the return flags
  344.         mov    bp, sp
  345.         xchg    ax, [bp+6]    ; get the flag byte.
  346.         or    ax, 1        ; set the carry bit
  347.         xchg    ax, [bp+6]    ; put it back.
  348.         pop    bp
  349.         mov    ax, 5        ; access denied
  350.         iret
  351. crit_err_handler endp
  352.  
  353.  
  354. ;-----------------------------------------------------------------------------
  355. ; Here we set the interrupted flag, and terminate the currently running
  356. ; process.
  357. ctl_brk_handler proc far
  358.         clc                ; make sure carry is clear
  359.         inc    cs:interrupted        ; set the flag
  360.  
  361. ; Make certain it isn't us that is going to get terminated.
  362. ; There is a small window where the in_exec flag is set but the child is
  363. ; not running yet, I assume that DOS doesn't test for ctl_brk at that time
  364. ; as it is bussily creating a new process.
  365.         cmp    cs:in_exec,0
  366.         je    just_return        ; note this implies CF == 0
  367.         stc                ; set CF to abort child
  368. just_return:    iret
  369. ctl_brk_handler endp
  370.  
  371.  
  372. ;-----------------------------------------------------------------------------
  373. ; Something really nasty happened, so abort the exec call and exit.
  374. ; This kills the calling process altogether, and is a very nasty way of
  375. ; termination since files may still be open etc.
  376. abort_exec_rhdr label near
  377.         mov    dx, offset error_rhdr
  378.         jmp    print_it
  379. abort_exec_rseg label near
  380.         mov    dx, offset error_rseg
  381.         jmp    print_it
  382. abort_exec_resize label near
  383.         mov    dx, offset error_resize
  384.         jmp    print_it
  385. abort_exec_free label near
  386.         mov    dx, offset error_free
  387.         jmp    print_it
  388. abort_exec_alloc label near
  389.         mov    dx, offset error_alloc
  390.         jmp    print_it
  391. abort_exec proc near
  392.         mov    dx, offset error_string
  393. print_it:    push    dx
  394.         mov    bx, [swap]
  395.         call    [free_resource+bx]
  396.         mov    ax, cs
  397.         mov    ds, ax
  398.         pop    dx
  399.         mov    ah, 9
  400.         int    21H
  401. kill_program:    mov    ax, 04cffH            ; nuke it!
  402.         int    21H
  403. abort_exec endp
  404.  
  405.  
  406. ;-----------------------------------------------------------------------------
  407. ; lodsw/stosw loop to copy data.  Called only for word copy operations.
  408. ;     ds:si  - point at source
  409. ;    es:di  - point at destination
  410. ;    cx     - count of bytes to copy.
  411. copy_data proc near
  412.         shr    cx, 1        ; convert to word count
  413.         jnc    copy_words
  414.         movsb
  415. copy_words:    rep    movsw        ; copy the words.
  416.         ret
  417. copy_data endp
  418.  
  419.  
  420.  
  421. ;=============================================================================
  422. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ XMS RECORDS.
  423. ;=============================================================================
  424. rhdr_xms proc near
  425.         ret
  426. rhdr_xms endp
  427.  
  428. rseg_xms proc near
  429.         ret
  430. rseg_xms endp
  431.  
  432. reset_xms_resource proc near
  433.         ret
  434. reset_xms_resource endp
  435.  
  436. free_xms_resource proc near
  437.         ret
  438. free_xms_resource endp
  439. ;=============================================================================
  440.  
  441.  
  442.  
  443. ;=============================================================================
  444. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ EMS RECORDS.
  445. ;=============================================================================
  446. rhdr_ems proc near
  447.         ret
  448. rhdr_ems endp
  449.  
  450. rseg_ems proc near
  451.         ret
  452. rseg_ems endp
  453.  
  454. reset_ems_resource proc near
  455.         ret
  456. reset_ems_resource endp
  457.  
  458. free_ems_resource proc near
  459.         ret
  460. free_ems_resource endp
  461. ;=============================================================================
  462.  
  463.  
  464.  
  465. ;=============================================================================
  466. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO READ FILE RECORDS.
  467. ;=============================================================================
  468. ; This routine reads a segment header from a file.
  469. ; The header is a seven byte record formatted as follows:
  470. ;    segment address        - of data
  471. ;    offset address        - of data
  472. ;     length in paragraphs    - of data
  473. ;    mode            - 1 => segment header (allocate seg on read)
  474. ;                  0 => subsegment, don't allocate on read.
  475. ; The information is placed into the tmpseg data area in the code segment.
  476. ; The routine aborts if an error is detected.
  477. rhdr_file proc near
  478.         mov    dx, offset tmpseg    ; read the header record out
  479.         mov    cx, 7
  480.         mov    bx, [tmphandle]
  481.         mov    ah, 03fH
  482.         int    21H
  483.         jnc    rhdr_done        ; make sure it worked
  484.         jmp    abort_exec_rhdr
  485.  
  486. rhdr_done:    cmp    ax, 7
  487.         je    exit_rhdr_file
  488.         or    ax, ax
  489.         je    signal_eof
  490.         jmp    abort_exec_rhdr
  491.  
  492. signal_eof:    stc
  493. exit_rhdr_file:    ret
  494. rhdr_file endp
  495.  
  496.  
  497. ;-----------------------------------------------------------------------------
  498. ; Read a segment from the temporary file whose handle is in cs:tmphandle.
  499. ; The routine aborts if an error is detected.
  500. rseg_file proc near
  501.         push    ds
  502.         mov    ds, word ptr cs:tmpseg; Now read the whole segment
  503.         mov    dx, word ptr cs:tmpseg+2
  504.         mov    cx, word ptr cs:tmpseg+4
  505.         mov    bx, cs:tmphandle
  506.         mov    ah, 03fH
  507.         int    21H
  508.         pop    ds
  509.         jnc    rseg_done
  510.         jmp    abort_exec_rseg
  511.  
  512. rseg_done:    cmp    ax, [word ptr tmpseg+4]
  513.         je    exit_rseg_file
  514.         jmp    abort_exec_rseg        ; If we didn't get read full
  515. exit_rseg_file:    ret                ; segment then abort
  516. rseg_file endp
  517.  
  518.  
  519. ;-----------------------------------------------------------------------------
  520. ; Seek to the beginning of the file.
  521. reset_file_resource proc near
  522.         mov    bx, [tmphandle]
  523.         xor    cx, cx
  524.         mov    dx, cx
  525.         mov    ax, 04200H        ; seek to begining of file
  526.         int    21H
  527.         ret
  528. reset_file_resource endp
  529.  
  530.  
  531. ;-----------------------------------------------------------------------------
  532. ; unlink the temporary file allocated for swapping.
  533. ; We close the file first, and then delete it.   We ignore errors here since
  534. ; we can't do anything about them anyway.
  535. free_file_resource proc near
  536.         mov    bx, [tmphandle]        ; get the file handle
  537.         mov    ah, 03eH        ; close the file
  538.         int    21H
  539.         mov    dx, offset tmpname    ; Now delete the temp file
  540.         mov    ah, 041H
  541.         int    21H
  542.         ret
  543. free_file_resource endp
  544. ;=============================================================================
  545.  
  546.  
  547.  
  548. ;=============================================================================
  549. ; CODE TO SWAP THE IMAGE IN FROM SECONDARY STORAGE
  550. ;=============================================================================
  551. swap_in proc near
  552.         mov    bx, [alstr]        ; get previous alloc strategy
  553.         mov    ax, 5801H        ; and set it back
  554.         int    21H
  555.         mov    bx, [swap]        ; get type of resource
  556.         call    [reset_resource+bx]    ; reset the resource
  557.         mov    es, [psp]        ; resize the program back
  558.         mov    bx, [progsize]        ; to original size
  559.         mov    ah, 04AH
  560.         int    21H
  561.         jnc    read_seg_loop
  562.         jmp    abort_exec
  563.  
  564. read_seg_loop:    mov    bx, [swap]        ; get type of resource
  565.         call    [read_header+bx]    ; get seg header
  566.         jc    exit_swap_in        ; all done
  567.         mov    al, [tmpseg+6]
  568.         cmp    al, seg_no_alloc    ; see if dummy segment header
  569.         je    read_seg_loop
  570.         cmp    al, seg_alloc        ; do we need to do an alloc?
  571.         jne    read_data        ; nope
  572.  
  573. ; Allocate back the memory for a segment that is not the [psp], note that this
  574. ; must come back to the same segment we had previously since other segments
  575. ; may have pointers stored in their variables that point to this segment using
  576. ; segment:offset long pointers.
  577.         mov    bx, [word ptr tmpseg+4]    ; get count of paragraphs
  578.         mov    ah, 048H        ; dos_alloc
  579.         int    21H
  580.         jc    alloc_error        ; oops!
  581.         cmp    ax, [word ptr tmpseg]    ; did we get the same segment?
  582.         je    read_seg_loop        ; yup!
  583. alloc_error:    jmp    abort_exec_alloc
  584.  
  585. read_data:    mov    bx, [swap]
  586.         call    [read_seg+bx]        ; this must succeed, if fail
  587.         jmp    read_seg_loop        ; we never come back here
  588.  
  589. exit_swap_in:    mov    bx, [swap]        ; all done, so free resource
  590.         call    [free_resource+bx]
  591.         ret
  592. swap_in endp
  593.  
  594.  
  595. ;=============================================================================
  596. ; CODE TO SWAP THE IMAGE OUT TO SECONDARY STORAGE
  597. ;=============================================================================
  598. ; This routine is called to swap the non-resident portion of the program
  599. ; out to the resource specified by the value of [cs:swap].  If the swap out
  600. ; fails, then appropriate routines are called to free the resources allocated
  601. ; up to that point.
  602. ;
  603. ; The steps used to swap the program out are as follows:
  604. ;    - calculate new size of program to remain resident and size to swap
  605. ;      out.
  606. ;    - write out non-resident portion of current segment
  607. ;    - walk DOS allocation chain and write out all other segments owned by
  608. ;      the current program that are contiguous with the _psp segment
  609. ;    - copy the environment down to low memory
  610. ;    - resize the current _psp segment to savesize
  611. ;    - free all segments belonging to program except current _psp segment
  612. swap_out proc near
  613.         mov    ax, 05800H    ; get memory alocation strategy
  614.         int    021H
  615.         mov    [alstr], ax    ; and save it for future restoration.
  616.         mov    di, [psp]    ; compute length of program to current
  617.         mov    bx, cs        ; value of cs, and find program size
  618.         sub    bx, di        ; by looking at length stored in
  619.         mov    ax, di        ; arena header found in front of psp
  620.         dec    ax
  621.         mov    es, ax
  622.         mov    si, es:3    ; si is size of program in paragraphs
  623.         mov    [progsize], si    ; progsize now contains the size.
  624.  
  625. ; Now compute length of program segment to save.
  626. ; Length is:   cs - psp + (offset overlay_code_here+15 >> 4)
  627.         mov    ax, offset overlay_code_here+15
  628.         shr    ax, 1
  629.         shr    ax, 1
  630.         shr    ax, 1
  631.         shr    ax, 1
  632.         add    bx, ax            ; bx is size of program to keep
  633.         sub    si, bx            ; si is # of paragraphs to save.
  634.         add    di, bx            ; di is paragraph to start at
  635.         mov    rootsize, bx
  636.         mov    resend, di        ; cs:resend is saved start para
  637.         mov    al, seg_no_alloc    ; set no allocation for segment
  638.         call    write_segment
  639.         jc    abort_swap_out
  640.  
  641. ; We have now saved the portion of the program segment that will not remain
  642. ; resident during the exec.  We should now walk the DOS allocation chain and
  643. ; write out all other segments owned by the current process.
  644. save_segments:    mov    ax, [psp]
  645.         dec    ax
  646.         mov    es, ax
  647.         mov    bx, offset write_segment_data
  648.         call    walk_arena_chain
  649.         jc    abort_swap_out
  650.  
  651. ; Now we must walk the chain of allocated memory blocks again and free
  652. ; all those that are owned by the current process, except the one that is
  653. ; the current process' psp.
  654. free_segments:    mov    ax, [psp]
  655.         dec    ax
  656.         mov    es,ax
  657.         mov    bx, offset free_dos_segment
  658.         call    walk_arena_chain
  659.         jnc    resize_program
  660.         jmp    abort_exec_free        ; can't fix it up now.
  661.  
  662. ; We now resize the program to the size specified by cs:rootsize.  This will
  663. ; free most of the memory taken up by the current program segment.
  664. resize_program: mov    es, [psp]        ; es is segment to resize.
  665.         mov    bx, [rootsize]        ; bx is size of segment.
  666.         mov    ah, 04aH        ; resize memory block
  667.         int    21H
  668.         jnc    swap_out_ok
  669.         jmp    abort_exec_resize    ; disaster
  670. swap_out_ok:    ret
  671.  
  672. ; The swap out failed for some reason, so free any allocated resources
  673. ; and set the carry bit.
  674. abort_swap_out:    mov    bx, [swap]
  675.         call    [free_resource+bx]
  676.         xor    ax, ax
  677.         mov    [swap], ax        ; clear the swap flag
  678.         stc
  679.         ret
  680. swap_out endp
  681.  
  682.  
  683. ;=============================================================================
  684. ; CODE TO SET-UP FOR AND EXEC THE CHILD PROCESS
  685. ;=============================================================================
  686. ; Actually execute the program.  If cs:swap is set, this code will invoke the
  687. ; swap-out/swap-in code as required.
  688. do_exec proc near
  689.         cmp    [swap], 0        ; does the user want to swap?
  690.         je    no_swap_out        ; nope
  691.         call    init_swap        ; figger out where to swap to
  692.         jc    no_swap_out        ; if carry set then don't swap
  693.         call    swap_out
  694.  
  695. no_swap_out:    cmp    [interrupted], 0    ; were we interrupted?
  696.         jne    leave_exec        ; yep, so clean up, don't exec
  697.  
  698. ; free passed in environment block if it is non zero.
  699. ; This way the parent program does not need to free it.
  700.         mov    ax, [envseg]
  701.         or    ax, ax
  702.         je    setup_block
  703.         push    ax
  704.         mov    es, ax
  705.         mov    ah, 49H
  706.         int    21H
  707.         pop    ax
  708.  
  709. ; set up the parameter block for the DOS exec call.
  710. ;    offset  contents
  711. ;        00  segment address of environment to be passed,
  712. ;          0 => use parents env.
  713. ;        02  pointer to command tail for new process.
  714. ;        06  pointer to fcb1
  715. ;        0a  pointer to fcb2
  716. setup_block:    mov    ax, [envseg]
  717.         mov    [ex_envseg], ax
  718.         mov    cx, cs
  719.         mov    [word ptr ex_cmdtail], offset cmdtail
  720.         mov    [word ptr ex_cmdtail+2], cx
  721.  
  722. ; set up registers for exec call
  723. ;    ds:dx    - pointer to pathname of program to execute
  724. ;    es:bx    - pointer to above parameter block
  725.         mov    dx, offset cmdpath
  726.         mov    es, cx
  727.         mov    bx, offset exec_block
  728.  
  729. ; Under DOS 2.x exec is notorious for clobbering registers and guarantees
  730. ; to preserve only cs:ip.
  731.         push    ds
  732.         mov    [ex_sp], sp
  733.         mov    [ex_ss], ss
  734.         mov    [ex_error], 0        ; clear exec error code
  735.         inc    [in_exec]        ; set internal flag
  736.         mov    ax, 04b00H
  737.         int    21H
  738.  
  739. ; returned from exec, so restore possibly clobbered registers.
  740.         mov    ss, cs:ex_ss
  741.         mov    sp, cs:ex_sp
  742.         pop    ds
  743.  
  744. ; check to make certain the exec call worked.
  745.         jnc    it_worked
  746.  
  747. ; exec call failed.  Save return code from msdos.
  748.         mov    [ex_error], ax
  749.         jmp    leave_exec
  750.  
  751. it_worked:    mov    ah, 04dH    ; get the return code
  752.         int    21H
  753.         cbw
  754.         mov    [eretcode], ax
  755.  
  756. leave_exec:    cmp    [swap], 0    ; check swap, if non-zero swap back in
  757.         je    no_swap_in
  758.         call    swap_in
  759.  
  760. ; Clear the in_exec after the swap back in.  This way we are guaranteed to
  761. ; get parent in and the resources freed should a ^C be hit when we are reading
  762. ; the image in.
  763. no_swap_in:    mov    [in_exec], 0
  764.         ret
  765. do_exec endp                
  766.  
  767.  
  768.  
  769. ;==============================================================================
  770. ; Everything past this point is overwriten with the environment and new
  771. ; program after the currently executing program is swapped out.
  772. ;==============================================================================
  773. overlay_code_here label word
  774.  
  775. ;-----------------------------------------------------------------------------
  776. ; Figure out where we can swap to and initialize the resource we are going to
  777. ; use.  We try XMS, EMS, and a tempfile (if specified), in that order.  We set
  778. ; [cs:swap] to the correct value based on which of the resources exists.
  779. ; If none can be used, then [cs:swap] is set to 0, and no swap takes place.
  780. ; The exec code will still attempt to execute the child in this instance, but
  781. ; may fail due to lack of resources.   Each swap_out_* routine must provide
  782. ; its own clean-up handler should it not be able to write all program
  783. ; segments to the swap resource.
  784. init_swap proc near
  785.         mov    [swap], 0
  786. ;call    init_xms
  787. ;jnc    init_done
  788. ;call    init_ems
  789. ;jnc    init_done
  790.         call    init_file
  791. init_done:    ret
  792. init_swap endp
  793.  
  794.  
  795. ;-----------------------------------------------------------------------------
  796. ; This routine is used to walk the DOS allocated memory block chain
  797. ; starting at address supplied in the es register.  For each block it
  798. ; calls the routine specified by the bx register with the segment length
  799. ; in si, and its address in di.  It does not apply the routine to the
  800. ; segment if the segment is the same as the current program's [cs:psp] value.
  801. memheader struc
  802.    magic    db    ?    ; either 'Z' for end or 'M' for allocated
  803.    owner    dw    ?    ; psp of owner block
  804.    len        dw    ?    ; length in paragraphs of segment
  805. memheader ends
  806.  
  807. walk_arena_chain proc near
  808.         mov    si, word ptr es:3        ; get length
  809.         mov    di, es
  810.         inc    di
  811.         mov    ax, word ptr es:1
  812.  
  813. ; Stop the search if the block is NOT owned by us.  Ignore our own psp block
  814. ; and our environment segment block.
  815.         cmp    ax, cs:psp            ; is it owned by us?
  816.         jne    walk_done            ; NOPE!  -- all done
  817.         cmp    di, cs:envseg            ; skip our environment
  818.         je    next_block
  819.         cmp    di, cs:psp            ; skip our psp
  820.         je    next_block
  821.  
  822. ; Now save state and call the routine pointed at by [bx].
  823.         push    di
  824.         push    si
  825.         push    bx
  826.         call    bx
  827.         pop    bx
  828.         pop    si
  829.         pop    di
  830.         jc    exit_walk            ; if error then stop
  831.         mov    al, byte ptr es:0        ; check if at end 
  832.         cmp    al, 'Z'
  833.         je    walk_done
  834.  
  835. next_block:    add    di, si                ; go on to next segment
  836.         mov    es, di
  837.         jmp    walk_arena_chain
  838. walk_done:    clc
  839. exit_walk:    ret
  840. walk_arena_chain endp
  841.  
  842.  
  843. ;-----------------------------------------------------------------------------
  844. ; This routine takes a dos segment found in the di register and free's it.
  845. free_dos_segment proc near
  846.         mov    es, di        ; free dos memory block
  847.         mov    ah, 49H
  848.         int    21H
  849.         ret
  850. free_dos_segment endp
  851.  
  852.  
  853. ;-----------------------------------------------------------------------------
  854. ; Called to invoke write_segment with proper values in the al register.  Only
  855. ; ever called from walk_arena_chain, and so al should be set to seg_alloc.
  856. write_segment_data label near
  857.         mov    al, seg_alloc    ; and fall through into write_segment
  858. ;-----------------------------------------------------------------------------
  859. ; This routine writes a segment as a block of data segments if the number of
  860. ; paragraphs to write exceeds 0x0fff (rarely the case).
  861. ; It stuffs the info into tmpseg, and then calls wheader and wseg to get the
  862. ; data out.
  863. ;
  864. ;    di:dx    segment:offset of segment;  offset is ALWAYS zero.
  865. ;    si    number of paragraphs to write.
  866. ;    al    mode of header to write
  867. write_segment proc near
  868.         push    di
  869.         push    si
  870.         xor    dx,dx
  871.         mov    bx, [swap]
  872.         call    [write_header+bx]
  873.         pop    si
  874.         pop    di
  875.         jc    exit_wseg
  876.  
  877. do_io_loop:    cmp    si, 0        ; are we done yet?
  878.         je    exit_wseg    ; yup so leave.
  879.         mov    cx, si        ; # of paragraphs to move
  880.         cmp    cx, 0fffH    ; see if we have lots to move?
  881.         jle    do_io
  882.         mov    cx, 0fffH    ; reset to max I/O size
  883.  
  884. do_io:        push    cx        ; save # of paragraphs we are writing
  885.         shl    cx, 1        ; shift cx by four to the left
  886.         shl    cx, 1
  887.         shl    cx, 1
  888.         shl    cx, 1
  889.         push    di        ; save the start, and count left
  890.         push    si
  891.         mov    si, cx
  892.         xor    dx,dx
  893.         mov    al, seg_data
  894.         mov    bx, [swap]
  895.         push    bx
  896.         call    [write_header+bx]
  897.         pop    bx
  898.         call    [write_seg+bx]
  899.         pop    si
  900.         pop    di
  901.         pop    dx        ; original paragraph count in dx
  902.         jc    exit_wseg    ; it failed so exit.
  903.         add    di, dx        ; adjust the pointers, and continue.
  904.         sub    si, dx
  905.         jmp     do_io_loop
  906. exit_wseg:    ret
  907. write_segment endp
  908.  
  909.  
  910. ;=============================================================================
  911. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE XMS RECORDS.
  912. ;=============================================================================
  913. init_xms proc near
  914.         ret
  915. init_xms endp
  916.  
  917. whdr_xms proc near
  918.         ret
  919. whdr_xms endp
  920.  
  921. wseg_xms proc near
  922.         ret
  923. wseg_xms endp
  924. ;=============================================================================
  925.  
  926.  
  927. ;=============================================================================
  928. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE EMS RECORDS.
  929. ;=============================================================================
  930. init_ems proc near
  931.         ret
  932. init_ems endp
  933.  
  934. whdr_ems proc near
  935.         ret
  936. whdr_ems endp
  937.  
  938. wseg_ems proc near
  939.         ret
  940. wseg_ems endp
  941. ;=============================================================================
  942.  
  943.  
  944. ;=============================================================================
  945. ; THE FOLLOWING SECTION DEALS WITH ALL ROUTINES REQUIRED TO WRITE FILES.
  946. ;=============================================================================
  947. ;-----------------------------------------------------------------------------
  948. ; Attempt to create a temporary file.  If the tempfile name is NIL then return
  949. ; with the cary flag set.
  950. init_file proc near
  951.         mov    al, [tmpname]
  952.         or    al, al
  953.         je    err_init_file
  954.         mov    dx, offset tmpname
  955.         xor     cx, cx
  956.         mov    ah, 03cH
  957.         int    21H
  958.         jc    err_init_file        ; if carry set then failure
  959.         mov    [tmphandle], ax        ; init swapping
  960.         mov    [swap], swap_file
  961.         jmp    exit_init_file
  962. err_init_file:    stc
  963. exit_init_file: ret
  964. init_file endp
  965.  
  966.  
  967. ;-----------------------------------------------------------------------------
  968. ; This routine writes a segment header to a file.
  969. ; The header is a seven byte record formatted as follows:
  970. ;    segment address        - of data
  971. ;    offset address        - of data
  972. ;     length in paragraphs    - of data
  973. ;    mode            - 1 => segment header (allocate seg on read)
  974. ;                  0 => subsegment, don't allocate on read.
  975. ; Routine takes three arguments:
  976. ;    di:dx    segment:offset of segment
  977. ;    si    number of paragraphs to write.
  978. ;    al    mode of header to write
  979. whdr_file proc near
  980.         mov    [word ptr tmpseg], di    ; save the segment/offset
  981.         mov    [word ptr tmpseg+2], dx
  982.         mov    [word ptr tmpseg+4], si    ; save the segment length
  983.         mov    [tmpseg+6], al
  984.         mov    dx, offset tmpseg    ; write the header record out
  985.         mov    cx, 7
  986.         mov    bx, [tmphandle]
  987.         mov    ah, 040H
  988.         int    21H
  989.         jc    exit_whdr_file        ; make sure it worked
  990.         cmp    ax, 7
  991.         je    exit_whdr_file        ; oh oh, disk is full!
  992. err_whdr_file:    stc
  993. exit_whdr_file:    ret
  994. whdr_file endp
  995.  
  996.  
  997. ;-----------------------------------------------------------------------------
  998. ; Write a segment to the temporary file whose handle is in cs:tmphandle
  999. ; Parameters for the write are assumed to be stored in the tmpseg data area.
  1000. ; function returns carry set if failed, carry clear otherwise.
  1001. wseg_file proc near
  1002.         push    ds
  1003.         mov    ds, word ptr cs:tmpseg ; Now write the whole segment
  1004.         mov    dx, word ptr cs:tmpseg+2
  1005.         mov    cx, word ptr cs:tmpseg+4
  1006.         mov    bx, cs:tmphandle
  1007.         mov    ah, 040H
  1008.         int    21H
  1009.         pop    ds
  1010.         jc    exit_wseg_file        ; make sure it worked
  1011.         cmp    ax, [word ptr tmpseg+4]
  1012.         je    exit_wseg_file
  1013. err_wseg_file:    stc                ; it failed (usually disk full)
  1014. exit_wseg_file:    ret
  1015. wseg_file endp
  1016. ;=============================================================================
  1017.  
  1018.  
  1019. ;=============================================================================
  1020. ; _exec: THIS IS THE MAIN ENTRY ROUTINE TO THIS MODULE
  1021. ;=============================================================================
  1022. ; This is the main entry routine into the swap code and corresponds to the
  1023. ; following C function call:
  1024. ;
  1025. ; exec( int swap, char far *program, char far *cmdtail, int environment_seg,
  1026. ;    char far *tmpfilename );
  1027. ;
  1028. ; Exec performs the following:
  1029. ;    1. set up the local code segment copies of arguments to the exec call.
  1030. ;    2. switch to a local stack frame so that we don't clobber the user
  1031. ;       stack.
  1032. ;    3. save old interrupt vectors for ctrl-brk.
  1033. ;    4. install our own handler for the ctrl-brk interrupt, our handler
  1034. ;       terminates the current running process, and returns with non-zero
  1035. ;       status code.
  1036. ;    5. get our psp
  1037. ;    6. setup arguments for exec call
  1038. ;    7. exec the program, save result code on return.
  1039. ;       8. restore previous ctrl-brk and crit-error handler.
  1040. ;       9. restore previous process stack, and segment registers.
  1041. ;      10. return from exec with child result code in AX
  1042. ;       and global _Interrupted flag set to true if child execution was
  1043. ;       interrupted.
  1044.  
  1045. ; NOTE:  When first called the segments here assume the standard segment
  1046. ;        settings.
  1047.         assume cs:@code, ds:DGROUP,es:DGROUP,ss:DGROUP
  1048.  
  1049.         public    _exec
  1050. _exec proc
  1051.             push    bp        ; set up the stack frame
  1052.         mov    bp, sp
  1053.         push    si        ; save registers we shouldn't step on.
  1054.         push    di
  1055.         push    ds
  1056.  
  1057. ; set up for copying of parameters passed in with long pointers.
  1058.         push    cs        ; going to use lodsb/stosb, set up es
  1059.         pop    es        ; as destination.
  1060.         assume  es:@code    ; let the assembler know :-)
  1061.         cld            ; make sure direction is right
  1062.  
  1063. ; Copy all parameters into the bottom of the code segment.  After doing so we
  1064. ; will immediately switch stacks, so that the user stack is preserved intact.
  1065.         mov    ax, ss:[a_swap]        ; save swap
  1066.         mov    es:swap, ax
  1067.         mov    ax, ss:[a_env]        ; save env seg to use
  1068.         mov    es:envseg, ax
  1069.  
  1070.         mov     di, offset cs:cmdpath    ; copy the command
  1071.         lds     si, ss:[a_prog]        ; 65 bytes worth
  1072.         mov    cx, 65
  1073.         call    copy_data
  1074.  
  1075.         mov    di, offset cs:cmdtail    ; copy the command tail
  1076.         lds    si, ss:[a_tail]        ; 129 bytes worth
  1077.         mov    cx, 129
  1078.         call    copy_data
  1079.  
  1080.         mov    di, offset cs:tmpname    ; copy the temp file name
  1081.         lds    si, ss:[a_tmp]        ; 65 bytes worth.
  1082.         mov    cx, 65
  1083.         call    copy_data
  1084.  
  1085. ; Now we save the current ss:sp stack pointer and swap stack to our temporary
  1086. ; stack located in the current code segment.  At the same time we reset the
  1087. ; segment pointers to point into the code segment only.
  1088. swap_stacks:    mov    ax, ss
  1089.         mov    es:old_ss, ax
  1090.         mov    es:old_sp, sp
  1091.         mov    ax, cs
  1092.         mov    ds, ax
  1093.         mov    ss, ax            ; set ss first, ints are then
  1094.         mov    sp, offset cs:exec_sp    ; disabled for this instr too
  1095.         assume  ds:@code, ss:@code    ; let the assembler know :-)
  1096.  
  1097. ; Now we save the old control break and critical error handler addresses.
  1098. ; We replace them by our own routines found in the resident portion of the
  1099. ; swapping exec code.
  1100. set_handlers:    mov    [interrupted], 0    ; clear interrupted flag
  1101.         mov    [eretcode], 0        ; clear the return code
  1102.         mov    ax, 03523H        ; get int 23 handler address
  1103.         int    21H
  1104.         mov    cs:old_ctl_brk_off, bx
  1105.         mov    cs:old_ctl_brk_seg, es
  1106.         mov    dx, offset ctl_brk_handler
  1107.         mov    ax, 02523H        ; set int 23 handler address
  1108.         int    21H
  1109.  
  1110.         mov    ax, 03524H        ; get int 24 handler address
  1111.         int    21H
  1112.         mov    cs:old_crit_err_off, bx
  1113.         mov    cs:old_crit_err_seg, es
  1114.         mov    dx, offset crit_err_handler
  1115.         mov    ax, 02524H        ; set int 24 handler address
  1116.         int    21H
  1117.  
  1118. ; Go and execute the child, we've set up all of its parameters.  The do_exec
  1119. ; routine will attempt to perform a swap of the code if requested to do so by
  1120. ; a non-zero value in the variable cs:swap.
  1121.         mov    ah, 051H        ; get the psp
  1122.         int    21H
  1123.         mov    cs:psp, bx
  1124.         call    do_exec
  1125.  
  1126. ; We're back from the exec, so fix things up the way they were.
  1127. ; Restore the old control-break and critical-error handlers.
  1128.         lds    dx, cs:old_ctl_brk
  1129.         mov    ax, 02523H
  1130.         int    21H
  1131.         lds    dx, cs:old_crit_err
  1132.         mov    ax, 02524H
  1133.         int    21H
  1134.  
  1135. ; Restore previous program stack segment registers, and data segment.
  1136.         mov    ax, cs:old_ss
  1137.         mov    ss, ax            ; mov into ss first, that way
  1138.         mov    sp, cs:old_sp        ; no interrupts in this instr.
  1139.         pop    ds
  1140.  
  1141. ; Tell the assembler we have swaped segments again.
  1142.         assume    ds:DGROUP,es:DGROUP,ss:DGROUP
  1143.  
  1144. ; Set the global Interrupted flag so that parent can tell it was interrupted.
  1145.         mov    ax, seg DGROUP:_Interrupted
  1146.         mov    es, ax
  1147.         mov    ax, cs:interrupted
  1148.         mov    es:_Interrupted, ax
  1149.  
  1150. ; Set the global errno value to reflect the success/failure of the DOS
  1151. ; exec call.
  1152.         mov    ax, seg DGROUP:_errno
  1153.         mov    es, ax
  1154.         mov    ax, cs:ex_error
  1155.         mov    es:_errno, ax
  1156.  
  1157. ; Fetch the child's return code, pop rest of stuff off of the stack
  1158. ; and return to the caller.
  1159.         mov    ax, cs:eretcode
  1160.         pop    di
  1161.         pop    si
  1162.         pop    bp
  1163.         ret
  1164. _exec endp
  1165.  
  1166. ; void do_hook_std_writes(int handle);
  1167. ;    This saves the 21h interrupt vector and changes it to point
  1168. ;    into this code.  Argument is the file handle of the -C file.
  1169.  
  1170.     public _do_hook_std_writes
  1171. _do_hook_std_writes proc
  1172.             push    bp
  1173.         mov    bp,sp
  1174.         push    di
  1175.  
  1176.         mov    di, ss:[a_handle]    ; handle of -C file
  1177.         mov    std_fil_handle, di
  1178.  
  1179.         mov    ah, 51h            ; request our PSP
  1180.         int    21h
  1181.         mov    [psp], bx        ; save it
  1182.  
  1183.         mov    es, bx
  1184.         les    bx, es:[34h]        ; pointer to job file table
  1185.         mov    al, es:[bx+1]        ; system file # of our stdout
  1186.         mov    [our_stdout], al
  1187.         mov    al, es:[bx+di]        ; system file number of -C file
  1188.         mov    std_fil_number, al
  1189.  
  1190.         mov    ax,3521h        ; request vector 21h
  1191.         int    21h            ; it's returned in ES:BX
  1192.         mov    word ptr [real_21h], bx
  1193.         mov    word ptr [real_21h+2], es
  1194.  
  1195.         push    ds
  1196.         mov    ax,cs
  1197.         mov    ds,ax
  1198.         lea    dx,our_21h_handler    ; DS:DX is the new vector
  1199.         mov    ax,2521h        ; set vector 21h
  1200.         int    21h
  1201.  
  1202.         pop    ds
  1203.         pop    di
  1204.         pop    bp
  1205.         ret
  1206. _do_hook_std_writes endp
  1207.  
  1208. ; void do_unhook_std_writes(void);
  1209. ;    This restores the 21h interrupt vector.
  1210. ;    The saved vector is zero if it wasn't changed (no -C option).
  1211.  
  1212.     public _do_unhook_std_writes
  1213. _do_unhook_std_writes proc
  1214.         push    ds
  1215.  
  1216.             lds    dx, [real_21h]    ; put saved vector into DS:DX
  1217.         mov    ax, ds
  1218.         or    ax, dx
  1219.         jz    unhook_return    ; zero means we didn't hook 21h
  1220.  
  1221.         mov    ax,2521h    ; set vector 21h
  1222.         simulate_21h
  1223.  
  1224. unhook_return:    pop ds
  1225.         ret
  1226. _do_unhook_std_writes endp
  1227. end
  1228.